home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / pcgen.asm < prev    next >
Encoding:
Assembly Source File  |  1994-08-10  |  17.8 KB  |  822 lines

  1. ; Collection of assembler support routines for NOS
  2. ; Copyright 1991 Phil Karn, KA9Q
  3.  
  4.     .MODEL    MEMMOD,C
  5.     LOCALS
  6.     %MACS
  7.     .LALL
  8.     extrn    ctick:proc
  9.     public    eoi
  10.  
  11. ; Hardware vector for timer linkage
  12. ; We use the timer hardware channel here instead of the indirect BIOS
  13. ; channel (1ch) because the latter is sluggish when running under DoubleDos
  14. TIMEVEC    EQU    08h
  15.  
  16.     .DATA
  17.     public    Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab
  18.     extrn    Isat:word
  19. Spsave    dw    ?        ; Save location for SP during interrupts
  20. Sssave    dw    ?        ; Save location for SS during interrupts
  21. Intstk    dw    512 dup(?)    ; Interrupt working stack
  22. Stktop    equ    $        ; SP set here when entering interrupt
  23. Mtasker    db    ?        ; Type of higher multitasker, if any
  24. Hashtab    db    256 dup(?)    ; Modulus lookup table for iphash()
  25.     .CODE
  26. dbase   dw      @data
  27. jtable    dw    l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15
  28.  
  29. ; Re-arm 8259 interrupt controller(s)
  30. ; Should be called just after taking an interrupt, instead of just
  31. ; before returning. This is because the 8259 inputs are edge triggered, and
  32. ; new interrupts arriving during an interrupt service routine might be missed.
  33. eoi    proc
  34.     cmp    Isat,1
  35.     jnz    @@1        ; Only one 8259, so skip this stuff
  36.     mov    al,0bh        ; read in-service register from
  37.     out    0a0h,al        ; secondary 8259
  38.     nop            ; settling delay
  39.     nop
  40.     nop
  41.     in    al,0a0h        ; get it
  42.     or    al,al        ; Any bits set?
  43.     jz    @@1        ; nope, not a secondary interrupt
  44.     mov    al,20h        ; Get EOI instruction
  45.     out    0a0h,al        ; Secondary 8259 (PC/AT only)
  46. @@1:    mov    al,20h        ; 8259 end-of-interrupt command
  47.     out    20h,al        ; Primary 8259
  48.     ret
  49. eoi    endp
  50.  
  51. ; common routine for interrupt return
  52.     public    doret
  53.     label    doret    far
  54.     pop    es
  55.     pop    di
  56.     pop    si
  57.     pop    bp
  58.     pop    dx
  59.     pop    cx
  60.     pop    bx
  61.     pop    ax
  62.     mov    ss,Sssave
  63.     mov    sp,Spsave    ; restore original stack context
  64.     pop    ds
  65.     iret
  66.  
  67. ; istate - return current interrupt state
  68.     public    istate
  69. istate    proc
  70.     pushf
  71.     pop    ax
  72.     and    ax,200h
  73.     jnz    @@1
  74.     ret
  75. @@1:    mov    ax,1
  76.     ret
  77. istate    endp
  78.  
  79. ; dirps - disable interrupts and return previous state:
  80. ;         0 = disabled, 1 = enabled
  81.     public dirps
  82. dirps    proc
  83.     pushf            ; save flags on stack
  84.     pop    ax            ; flags -> ax
  85.     and    ax,200h        ; 1<<9 is IF bit
  86.     jz    @@1            ; ints are already off; return 0
  87.     mov    ax,1
  88.     cli                ; interrupts now off
  89. @@1:    ret
  90. dirps    endp
  91.  
  92. ; restore - restore interrupt state:
  93. ;           0 = off, nonzero = on
  94.     public    restore
  95. restore    proc
  96.     arg is:word
  97.     test    is,0ffffh
  98.     jz    @@1
  99.     sti
  100.     ret
  101. @@1:    cli            ; should already be off, but just in case...
  102.     ret
  103. restore    endp
  104.  
  105.  
  106. ; multitasker types
  107. NONE        equ    0
  108. DOUBLEDOS    equ    1
  109. DESQVIEW    equ    2
  110. WINDOWS3    equ    3
  111.  
  112. ; Relinquish processor so other task can run
  113.     public    giveup
  114. giveup    proc
  115.     pushf        ;save caller's interrupt state
  116.     sti        ;re-enable interrupts
  117.         cmp     Mtasker, DOUBLEDOS
  118.     jnz    @@1
  119.     mov    al,2    ; 110 ms
  120.     mov    ah,0eeh
  121.     int    21h
  122.     popf        ; restore caller's interrupt state
  123.     ret
  124.  
  125. @@1:    cmp     Mtasker, DESQVIEW
  126.     jnz    @@2
  127.     mov    ax, 1000h
  128.     int    15h
  129.     popf        ; restore interrupts
  130.     ret
  131.  
  132. @@2:    cmp     Mtasker, WINDOWS3
  133.     jnz    @@3
  134.     mov    ax, 1680h
  135.     int    2fh
  136.     cmp    al, 80h    ; call supported?
  137.     jz    @@3    ; nope
  138.     popf        ; yes - restore interrupts
  139.     ret
  140.  
  141. @@3:    hlt        ; wait for an interrupt
  142.     popf        ; restore interrupts
  143.     ret
  144. giveup    endp
  145.  
  146. ; check for a multitasker running
  147.     public    chktasker
  148. chktasker    proc
  149.         mov     Mtasker,NONE
  150.  
  151.     ; Check for Microsoft Windows
  152.     mov    ax,1600h
  153.     int    2fh
  154.     cmp    al, 00h        ; 0 means windows multitasking not running
  155.     jz    @@4
  156.     cmp    al, 80h        ; ditto for 80h return
  157.     jz    @@4
  158.         mov     Mtasker, WINDOWS3
  159.     ret
  160.  
  161.     ; Check for DoubleDos
  162. @@4:    mov    ah,0e4h
  163.     int    21h
  164.     cmp    al,1
  165.     jz    @@1
  166.     cmp    al,2
  167.     jnz    @@2
  168. @@1:    mov     Mtasker, DOUBLEDOS
  169.     ret
  170.  
  171.     ; Check for DESQVIEW
  172. @@2:    mov    ax, 2b01h
  173.     mov    cx, 4445h
  174.     mov    dx, 5351h
  175.     int    21h
  176.     cmp    al, 0ffh
  177.     jnz    @@3
  178.     ret
  179.  
  180. @@3:    mov     Mtasker, DESQVIEW
  181.     ret
  182. chktasker    endp
  183.  
  184. ; getss - Read SS for debugging purposes
  185.     public    getss
  186. getss    proc
  187.     mov    ax,ss
  188.     ret
  189. getss    endp
  190.  
  191. ; clockbits - Read low order bits of timer 0 (the TOD clock)
  192. ; This works only for the 8254 chips used in ATs and 386s.
  193. ;
  194. ; The timer runs in mode 3 (square wave mode), counting down
  195. ; by twos, twice for each cycle. So it is necessary to read back the
  196. ; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
  197. ; pin forms the most significant bit of the count. Unfortunately,
  198. ; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
  199. ;
  200. ; The PC's clock design is soooo brain damaged...
  201.  
  202.     public    clockbits
  203. clockbits    proc
  204.     mov    al,0c2h    ; latch timer 0 count and status for reading
  205.     pushf
  206.     cli        ; make chip references atomic
  207.     out    43h,al    ; send latch command
  208.     in    al,40h    ; get status of timer 0
  209.     mov    bl,al    ; save status
  210.     in    al,40h    ; get lsb of count
  211.     mov    ah,al    ; save lsb
  212.     in    al,40h    ; get msb of count
  213.     popf        ; no more chip references
  214.     and    bl,80h    ; we're only interested in the OUT bit
  215.     xchg    ah,al    ; ax = count in correct order
  216.     shr    ax,1    ; count /= 2
  217.     jz    @@3    ; zero count requires carry propagation
  218. @@2:    or    ah,bl    ; combine with OUT bit as most sig bit of count
  219.     ret
  220. @@3:    xor    bl,80h    ; propagate carry by toggling OUT bit when cnt == 0
  221.     or    ah,bl    ; combine with !OUT bit as most sig bit of count
  222.     ret
  223.  
  224. clockbits    endp
  225.  
  226. ; Internet checksum subroutine
  227. ; Compute 1's-complement sum of data buffer
  228. ; Uses an unwound loop inspired by "Duff's Device" for performance
  229. ;
  230. ; Called from C as
  231. ; unsigned short
  232. ; lcsum(buf,cnt)
  233. ; unsigned short *buf;
  234. ; unsigned short cnt;
  235.     public    lcsum
  236. lcsum    proc
  237.     arg    buf:ptr,cnt:word
  238.  
  239.         if      @DataSize NE 0
  240.         uses    ds,si
  241.         lds    si,buf    ; ds:si = buf
  242.     else
  243.         uses    si
  244.         mov    si,buf    ; ds:si = buf (ds already set)
  245.     endif
  246.  
  247.     mov    cx,cnt        ; cx = cnt
  248.     cld            ; autoincrement si
  249.     mov    ax,cx
  250.     shr    cx,1        ; cx /= 16, number of loop iterations
  251.     shr    cx,1
  252.     shr    cx,1
  253.     shr    cx,1
  254.  
  255.     inc    cx        ; make fencepost adjustment for 1st pass
  256.     and    ax,15        ; ax = number of words modulo 16
  257.     shl    ax,1        ; *=2 for word table index
  258.     lea    bx,jtable    ; bx -> branch table
  259.     add    bx,ax        ; index into jump table
  260.     clc            ; initialize carry = 0
  261.     mov    dx,0        ; clear accumulated sum
  262.     jmp    word ptr cs:[bx]    ; jump into loop
  263.  
  264. ; Here the real work gets done. The numeric labels on the lodsw instructions
  265. ; are the targets for the indirect jump we just made.
  266. ;
  267. ; Each label corresponds to a possible remainder of (count / 16), while
  268. ; the number of times around the loop is determined by the quotient.
  269. ;
  270. ; The loop iteration count in cx has been incremented by one to adjust for
  271. ; the first pass.
  272. deloop:    lodsw
  273.     adc    dx,ax
  274. l15:    lodsw
  275.     adc    dx,ax
  276. l14:    lodsw
  277.     adc    dx,ax
  278. l13:    lodsw
  279.     adc    dx,ax
  280. l12:    lodsw
  281.     adc    dx,ax
  282. l11:    lodsw
  283.     adc    dx,ax
  284. l10:    lodsw
  285.     adc    dx,ax
  286. l9:    lodsw
  287.     adc    dx,ax
  288. l8:    lodsw
  289.     adc    dx,ax
  290. l7:    lodsw
  291.     adc    dx,ax
  292. l6:    lodsw
  293.     adc    dx,ax
  294. l5:    lodsw
  295.     adc    dx,ax
  296. l4:    lodsw
  297.     adc    dx,ax
  298. l3:    lodsw
  299.     adc    dx,ax
  300. l2:    lodsw
  301.     adc    dx,ax
  302. l1:    lodsw
  303.     adc    dx,ax
  304. l0:    loop    deloop        ; :-)
  305.  
  306.     adc    dx,0        ; get last carries
  307.     adc    dx,0
  308.     mov    ax,dx        ; result into ax
  309.     xchg    al,ah        ; byte swap result (8088 is little-endian)
  310.     ret
  311. lcsum    endp
  312.  
  313. ; Link timer handler into timer chain
  314. ; Arg == address of timer handler routine
  315. ; MUST be called exactly once before uchtimer is called!
  316.  
  317. toff    dw    ?        ; save location for old vector
  318. tseg    dw    ?        ;  must be in code segment
  319.  
  320.     public    chtimer
  321. chtimer    proc
  322.     arg    vec:far ptr
  323.     uses    ds
  324.  
  325.     mov    ah,35h        ; get current vector
  326.     mov    al,TIMEVEC
  327.     int    21h        ; puts vector in es:bx
  328.     mov    cs:tseg,es    ; stash
  329.     mov    cs:toff,bx
  330.  
  331.     mov    ah,25h
  332.     mov    al,TIMEVEC
  333.     lds    dx,vec        ; ds:si = vec
  334.  
  335.     int    21h        ; set new vector
  336.     ret
  337. chtimer    endp
  338.  
  339. ; unchain timer handler from timer chain
  340. ; MUST NOT be called before chtimer!
  341.     public    uchtimer
  342. uchtimer    proc
  343.     uses    ds
  344.  
  345.     mov    ah,25h
  346.     mov    al,TIMEVEC
  347.     mov    dx,toff
  348.     mov    ds,tseg
  349.     int    21h        ; restore old vector
  350.     ret
  351. uchtimer    endp
  352.  
  353. ; Clock tick interrupt handler. Note the use of "label" rather than "proc"
  354. ; here, necessitated by the fact that "proc" automatically generates BP-saving
  355. ; code that we don't want here.
  356.  
  357.     public    btick
  358.     label    btick    far
  359.  
  360.     pushf
  361.     push    ds
  362.     cli
  363.     mov    ds,cs:dbase    ; establish interrupt data segment
  364.  
  365.     mov    Sssave,ss    ; stash user stack context
  366.     mov    Spsave,sp
  367.  
  368.     mov    ss,cs:dbase
  369.     lea    sp,Stktop
  370.  
  371.     push    ax        ; save user regs on interrupt stack
  372.     push    bx
  373.     push    cx
  374.     push    dx
  375.     push    bp
  376.     push    si
  377.     push    di
  378.     push    es
  379.  
  380.     call    ctick
  381.  
  382.      pop    es
  383.     pop    di
  384.     pop    si
  385.     pop    bp
  386.     pop    dx
  387.     pop    cx
  388.     pop    bx
  389.     pop    ax
  390.     mov    ss,Sssave
  391.     mov    sp,Spsave    ; restore original stack context
  392.     pop    ds
  393.     popf
  394.     jmp    dword ptr [toff]        ; link to previous vector
  395.  
  396. ; Convert 32-bit int in network order to host order (dh, dl, ah, al)
  397. ; Called from C as
  398. ; int32 get32(char *cp);
  399.  
  400.     public    get32
  401. get32    proc
  402.     arg    cp:ptr
  403.         if      @DataSize NE 0
  404.         uses    ds,si
  405.         lds    si,cp    ; ds:si = cp
  406.     else
  407.         uses    si
  408.         mov    si,cp    ; ds:si = cp (ds already set)
  409.     endif
  410.  
  411.     cld
  412.     lodsw
  413.     mov    dh,al    ; high word to dx, a-swapping as we go
  414.     mov    dl,ah
  415.     lodsw
  416.     xchg    al,ah    ; low word stays in ax, just swap
  417.     ret
  418. get32    endp
  419.  
  420. ; Convert 16-bit int in network order to host order (ah, al)
  421. ; Called from C as
  422. ; int16 get16(char *cp);
  423.  
  424.     public    get16
  425. get16    proc
  426.     arg    cp:ptr
  427.         if      @DataSize NE 0
  428.         uses    ds,si
  429.         lds    si,cp    ; ds:si = cp
  430.     else
  431.         uses    si
  432.         mov    si,cp    ; ds:si = cp (ds already set)
  433.     endif
  434.  
  435.     lodsw        ; note: direction flag is don't-care
  436.     xchg    al,ah    ; word stays in ax, just swap
  437.     ret
  438. get16    endp
  439.  
  440. ; Convert 32-bit int to network order, returning new pointer
  441. ; Called from C as
  442. ; char *put32(char *cp,int32 x);
  443.  
  444.     public    put32
  445. put32    proc
  446.     arg    cp:ptr,x:dword
  447.         if      @DataSize NE 0
  448.         uses    ds,di
  449.         les    di,cp    ; es:di = cp
  450.         mov    ax,ss    ; our parameter is on the stack, and ds might not
  451.         mov    ds,ax    ;   be pointing to ss.
  452.     else
  453.         uses    di
  454.         mov    di,cp    ; es:di = cp
  455.         mov    ax,ds    ; point es at data segment
  456.         mov    es,ax
  457.     endif
  458.  
  459.     cld
  460.     mov    ax,word ptr (x+2)    ; read high word of machine version
  461.     xchg    ah,al            ; swap bytes
  462.     stosw                ; output in network order
  463.     mov    ax,word ptr x        ; read low word of machine version
  464.     xchg    ah,al            ; swap bytes
  465.     stosw                ; put in network order
  466.  
  467.     mov    ax,di    ; return incremented output pointer
  468.         if      @DataSize NE 0
  469.         mov    dx,es    ; upper half of pointer
  470.     endif
  471.     ret
  472. put32    endp
  473.  
  474. ; Convert 16-bit int to network order, returning new pointer
  475. ; Called from C as
  476. ; char *put16(char *cp,int16 x);
  477.  
  478.     public    put16
  479. put16    proc
  480.     arg    cp:ptr,x:word
  481.     uses    di
  482.         if      @DataSize NE 0
  483.         les    di,cp    ;es:di = cp
  484.     else
  485.         mov    di,cp    ; es:di = cp
  486.         mov    ax,ds
  487.         mov    es,ax
  488.     endif
  489.     cld
  490.     mov    ax,x    ; fetch source word in machine order
  491.     xchg    ah,al    ; swap bytes
  492.     stosw        ; save in network order
  493.     mov    ax,di    ; return new output pointer to user
  494.         if      @DataSize NE 0
  495.         mov    dx,es    ; upper half of pointer
  496.     endif
  497.     ret
  498. put16    endp
  499.  
  500. ; kbraw - raw, nonblocking read from console
  501. ; If character is ready, return it; if not, return -1
  502.     public    kbraw
  503. kbraw    proc
  504.     mov    ah,06h    ; Direct Console I/O
  505.     mov    dl,0ffh    ; Read from keyboard
  506.     int    21h    ; Call DOS
  507.     jz    @@1    ; zero flag set -> no character ready
  508.     mov    ah,0    ; valid char is 0-255
  509.     ret
  510. @@1:    mov    ax,-1    ; no char, return -1
  511.     ret
  512. kbraw    endp
  513.  
  514. if      @Cpu AND 2
  515. ; fast I/O buffer routines
  516. ; version for 80186, 286, 386 (uses ins, outs instructions)
  517.  
  518. ; outbuf - put a buffer to an output port
  519.     public    outbuf
  520. outbuf    proc
  521.     arg    port:word,buf:ptr,cnt:word
  522.         if      @DataSize NE 0
  523.         uses    ds,si
  524.         lds    si,buf    ; ds:si = buf
  525.     else
  526.         uses    si
  527.         mov    si,buf    ;ds:si = buf (ds already set)
  528.     endif
  529.     mov    dx,port
  530.     mov    cx,cnt
  531.     cld
  532.     rep outsb        ; works only on PC/AT (80286)
  533.     mov    dx,ds
  534.     mov    ax,si        ; return pointer just past end of buffer
  535.     ret
  536. outbuf    endp
  537.  
  538. ; inbuf - get a buffer from an input port
  539.     public    inbuf
  540. inbuf    proc
  541.     arg    port:word,buf:ptr,cnt:word
  542.     uses    di
  543.         if      @DataSize NE 0
  544.         les    di,buf        ; es:di = buf
  545.     else
  546.         mov    di,buf        ; es:di = buf
  547.         mov    ax,ds
  548.         mov    es,ax
  549.     endif
  550.     mov    dx,port
  551.     mov    cx,cnt
  552.     cld
  553.     rep insb        ; works only on PC/AT (80286)
  554.     mov    dx,es
  555.     mov    ax,di        ; return pointer just past end of buffer
  556.     ret
  557. inbuf    endp
  558.  
  559. else
  560.  
  561. ; fast buffer I/O routines
  562. ; version for 8086/8
  563.  
  564. ; outbuf - put a buffer to an output port
  565.     public    outbuf
  566. outbuf    proc
  567.     arg    port:word,buf:ptr,cnt:word
  568.         if      @DataSize NE 0
  569.         uses    ds,si
  570.         lds    si,buf    ; ds:si = buf
  571.     else
  572.         uses    si
  573.         mov    si,buf    ; ds:si = buf (ds already set)
  574.     endif
  575.  
  576.     mov    dx,port
  577.     mov    cx,cnt
  578.     cld
  579.  
  580. ; If buffer doesn't begin on a word boundary, send the first byte
  581.     test    si,1    ; (buf & 1) ?
  582.     jz    @@even ; no
  583.     lodsb        ; al = *si++;
  584.     out    dx,al    ; out(dx,al);
  585.     dec    cx    ; cx--;
  586.     mov    cnt,cx    ; save for later test
  587. @@even:
  588.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  589. ; Do the bulk of the buffer, a word at a time
  590.     jcxz    @@nobuf    ; if(cx != 0){
  591. @@deloop:
  592.     lodsw        ; do { ax = *si++; (si is word pointer)
  593.     out    dx,al    ; out(dx,lowbyte(ax));
  594.     mov    al,ah
  595.     out    dx,al    ; out(dx,hibyte(ax));
  596.     loop    @@deloop    ; } while(--cx != 0); }
  597. ; now check for odd trailing byte
  598. @@nobuf:
  599.     mov    cx,cnt
  600.     test    cx,1
  601.     jz    @@cnteven
  602.     lodsb        ; al = *si++;
  603.     out    dx,al
  604. @@cnteven:
  605.     mov    dx,ds
  606.     mov    ax,si        ; return pointer just past end of buffer
  607.     ret
  608. outbuf    endp
  609.  
  610. ; inbuf - get a buffer from an input port
  611.     public    inbuf
  612. inbuf    proc
  613.     arg port:word,buf:ptr,cnt:word
  614.     uses    di
  615.         if      @DataSize NE 0
  616.         les    di,buf    ; es:di = buf
  617.     else
  618.         mov    di,buf    ; es:di = buf
  619.         mov    ax,ds
  620.         mov    es,ax
  621.     endif
  622.     mov    dx,port
  623.     mov    cx,cnt
  624.     cld
  625.  
  626. ; If buffer doesn't begin on a word boundary, get the first byte
  627.     test    di,1    ; if(buf & 1){
  628.     jz    @@bufeven ;
  629.     in    al,dx    ; al = in(dx);
  630.     stosb        ; *di++ = al
  631.     dec    cx    ; cx--;
  632.     mov    cnt,cx    ; cnt = cx; } save for later test
  633. @@bufeven:
  634.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  635. ; Do the bulk of the buffer, a word at a time
  636.     jcxz    @@nobuf    ; if(cx != 0){
  637. @@deloop:
  638.     in    al,dx    ; do { al = in(dx);
  639.     mov    ah,al
  640.     in    al,dx    ; ah = in(dx);
  641.     xchg    al,ah
  642.     stosw        ; *si++ = ax; (di is word pointer)
  643.     loop    @@deloop    ; } while(--cx != 0);
  644. ; now check for odd trailing byte
  645. @@nobuf:
  646.     mov    cx,cnt
  647.     test    cx,1
  648.     jz    @@cnteven
  649.     in    al,dx
  650.     stosb        ; *di++ = al
  651. @@cnteven:
  652.     mov    dx,es
  653.     mov    ax,di        ; return pointer just past end of buffer
  654.     ret
  655. inbuf    endp
  656.  
  657. endif
  658.  
  659.     public    longdiv
  660.  
  661. ; long unsigned integer division - divide an arbitrary length dividend by
  662. ; a 16-bit divisor. Replaces the dividend with the quotient and returns the
  663. ; remainder. Called from C as
  664. ;
  665. ; unsigned short
  666. ; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
  667. ;
  668. ;Register usage:
  669. ; di - divisor
  670. ; si - pointer into dividend array
  671. ; cx - loop counter, initialized to the number of 16-bit words in the dividend
  672. ; ax - low word of current dividend before each divide, current quotient after
  673. ; dx - remainder from previous divide carried over, becomes high word of
  674. ;      dividend for next divide
  675.  
  676. longdiv    proc
  677.     arg    divisor:word,cnt:word,dividend:ptr
  678.         if      @DataSize NE 0
  679.         uses    ds,si,di
  680.         lds    si,dividend
  681.     else
  682.         uses    si,di
  683.         mov    si,dividend    ;si -> dividend array
  684.     endif
  685.  
  686.     cmp    divisor,0        ; divisor == 0?
  687.     jne    @2            ; no, ok
  688.     xor    ax,ax            ; yes, avoid divide-by-zero trap
  689.     jmp    short @1
  690.  
  691. @2:    mov    dx,0            ; init remainder = 0
  692.     mov    cx,cnt            ; init cnt
  693.     mov    di,divisor        ; cache divisor in register
  694.  
  695. @@deloop:
  696.     mov    ax,word ptr [si]    ; fetch current word of dividend
  697.     cmp    ax,0            ; dividend == 0 ?
  698.     jne    @7            ; nope, must do division
  699.     cmp    dx,0            ; remainder also == 0?
  700.     je    @4            ; yes, skip division, continue
  701.  
  702. @7:    div    di            ; do division
  703.     mov    word ptr [si],ax    ; save quotient
  704.  
  705. @4:    inc    si            ; next word of dividend
  706.     inc    si
  707.     loop     @@deloop
  708.  
  709.     mov    ax,dx            ; return last remainder
  710. @1:    ret
  711.  
  712. longdiv    endp
  713.  
  714. ; long unsigned integer multiplication - multiply an arbitrary length
  715. ; multiplicand by a 16-bit multiplier, leaving the product in place of
  716. ; the multipler, returning the carry. Called from C as
  717. ;
  718. ; unsigned short
  719. ; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
  720. ;
  721. ; Register usage:
  722. ; di = multiplier
  723. ; si = pointer to current word of multiplicand
  724. ; bx = carry from previous round
  725. ; cx = count of words in multiplicand
  726. ; dx,ax = scratch for multiply
  727.  
  728.     public longmul
  729. longmul    proc    far
  730.     arg    multiplier:word,n:word,multiplicand:ptr
  731.         if      @DataSize NE 0
  732.         uses    ds,si,di
  733.         lds    si,multiplicand
  734.     else
  735.         uses    si,di
  736.         mov    si,multiplicand    ; si -> multiplicand array
  737.     endif
  738.  
  739.     mov    di,multiplier        ; cache multiplier in register
  740.     xor    bx,bx            ; init carry = 0
  741.     mov    cx,n            ; fetch n
  742.     mov    ax,cx
  743.     shl    ax,1            ; *2 = word offset
  744.     add    si,ax            ; multiplicand += n
  745.  
  746. @@deloop:
  747.     dec    si
  748.     dec    si            ; work from right to left
  749.     mov    ax,word ptr [si]    ; fetch current multiplicand
  750.     or    ax,ax            ; skip multiply if zero
  751.     jz    @@nomult
  752.     mul    di            ; dx:ax <- ax * di
  753. @@nomult:
  754.     add    ax,bx            ; add carry from previous multiply
  755.     mov    word ptr [si],ax    ; save low order word of product
  756.     mov    bx,0            ; clear previous carry, leaving CF alone
  757.     adc    bx,dx            ; save new carry
  758.     xor    dx,dx            ; clear in case we skip the next mult
  759.     loop    @@deloop
  760.  
  761.     mov    ax,bx            ; return final carry
  762.     ret
  763. longmul    endp
  764.  
  765. ifdef    notdef
  766. ; divide 32 bits by 16 bits, returning both quotient and remainder
  767. ; This allows C programs that need both to avoid having to do two divisions
  768. ;
  769. ; Called from C as
  770. ;    long divrem(dividend,divisor)
  771. ;    long dividend;
  772. ;    short divisor;
  773. ;
  774. ;    The quotient is returned in the low 16 bits of the result,
  775. ;    and the remainder is returned in the high 16 bits.
  776.  
  777.     public    divrem
  778. divrem    proc
  779.     arg    dividend:dword,divisor:word
  780.     mov    ax,word ptr dividend
  781.     mov    dx,word ptr (dividend+2)
  782.     div    divisor
  783.     ret
  784. divrem    endp
  785. endif    
  786.  
  787. ; General purpose hash function for IP addresses
  788. ; Uses lookup table Hashtab[] initialized in ip.c
  789. ; Called from C as
  790. ; char hash_ip(int32 ipaddr);
  791.  
  792.     public hash_ip
  793. hash_ip    proc
  794.     arg    ipaddr:dword
  795.     lea    bx,Hashtab
  796.     mov    ax,word ptr ipaddr
  797.     xor    ax,word ptr (ipaddr+2)
  798.     xor    al,ah
  799.     xlat
  800.     xor    ah,ah
  801.     ret
  802. hash_ip    endp
  803.  
  804. ; Compute int(log2(x))
  805. ; Called from C as
  806. ; int log2(int16 x);
  807.  
  808.     public    log2
  809. log2    proc
  810.     arg    x:word
  811.     mov    cx,16
  812.     mov    ax,x
  813. @@2:    rcl    ax,1 
  814.     jc    @@1
  815.     loop    @@2
  816. @@1:    dec    cx
  817.     mov    ax,cx
  818.     ret
  819. log2    endp
  820.     end
  821.